﻿#region << 文件说明 >>

/*----------------------------------------------------------------
 * 命名空间：Vampirewal.Core.MiniApi
 * 唯一标识：80e600ed-f55d-4ed6-8a4c-1284d08d62ab
 * 文件名：VampirewalMiniApiBootStartup
 *
 * 创建者：杨程
 * 电子邮箱：235160615@qq.com
 * 创建时间：2023/12/18 9:48:24
 * 版本：V1.0.0
 * 描述：
 *
 * ----------------------------------------------------------------
 * 修改人：
 * 时间：
 * 修改说明：
 *
 * 版本：V1.0.1
 *----------------------------------------------------------------*/

#endregion << 文件说明 >>

global using Microsoft.AspNetCore.Builder;
global using Microsoft.AspNetCore.Http;
global using Microsoft.Extensions.DependencyInjection;
global using System.Linq.Expressions;
global using System.Net;
global using System.Reflection;
global using Vampirewal.Core.Components;
global using Vampirewal.Core.Interface;
global using Vampirewal.Core.SimpleMVVM;

namespace Vampirewal.Core.MiniApi;

/// <summary>
/// MiniApi启动器
/// </summary>
public abstract class VampirewalMiniApiBootStartup : VampirewalBootStartUp, IVampirewalBootStartUp
{
    /// <summary>
    /// 构建BootStartup并启动
    /// <para><typeparamref name="框架已自动调用请勿重复调用"/></para>
    /// </summary>
    /// <exception cref="BootStartUpException"></exception>
    public override void BuildBootStartUp(string[] args)
    {
        if (IsStart)
        {
            throw new BootStartUpException("请勿重复调用！");
        }

        GetStartArgs(args);

        var webApplicationBuilder = WebApplication.CreateBuilder();

        RegisterService(webApplicationBuilder.Services);

        RegisterAll(webApplicationBuilder.Services);

        RegisterMiniApiService(webApplicationBuilder.Services);

        #region 这个是MiniApi在7.0开始自带的异常捕获，感觉不是很好用，无法做到统一返回值

        if (webApplicationBuilder.Services.Any(f => f.ServiceType == typeof(IProblemDetailsWriter)))
            webApplicationBuilder.Services.AddProblemDetails();

        #endregion

        
        //webApplicationBuilder.Host.ConfigureServices((ctx, ser) =>
        //{
        //    foreach (var item in Collection)
        //    {
        //        ser.Add(item);
        //    }
        //});

        webApplication = webApplicationBuilder.Build();

        #region 还是交给调用框架的人去选择用不用吧

        //自己实现一个全局异常捕获中间件，放在最开始的地方
        //webApplication.UseMiddleware<ExceptionHandlerMiddleware>();

        #endregion

        Configure(webApplication);

        #region 将标记Controller特性的类中的方法注册成api

        var resultProvider = webApplication.Services.GetService<IVampirewalMiniApiResultProvider>();

        foreach (var item in MiniApiClass)
        {
            WebApplicationBuilderExtension.AutoMapRoute(item, webApplication, resultProvider);
        }

        #endregion

        #region 这个是MiniApi在7.0开始自带的异常捕获，不是很好用

        //webApplication.UseExceptionHandler(exceptionHandlerApp =>
        //{
        //    exceptionHandlerApp.Run(async context=>
        //    {
        //        await Results.Problem("异常了").ExecuteAsync(context);
        //    });
        //});

        #endregion

        SetServiceProvider(webApplication.Services);

        VampirewalCoreContext.GetInstance().SetService(Services);

        

        var option = webApplication.Services.GetRequiredService<IConfigContainer<VampirewalMiniApiOptions>>().Value;

        if (option.IsWPF)
        {
            WindowsManager.GetInstance().OpenWindow(FirstViewKey, IsMainWindow: true);
        }

        IsStart = true;

        if (option.Urls == null)
        {
            option.Urls = new List<string>() { "http://*:9090" };

            VampirewalCoreContext.GetInstance().SetOptions(option);
        }

        foreach (var item in option.Urls)
        {
            webApplication.Urls.Add(item);
        }
    }

    /// <summary>
    /// 配置中间件
    /// </summary>
    /// <param name="app"></param>
    public abstract void Configure(IApplicationBuilder app);

    private List<Type> MiniApiClass { get; set; } = new List<Type>();

    /// <summary>
    /// 注册Miniapi服务
    /// </summary>
    private void RegisterMiniApiService(IServiceCollection services)
    {
        var types = AppDomain.CurrentDomain.GetAssemblies().SelectMany(a => a.GetTypes()).Where(w => w.GetCustomAttribute<VampirewalControllerAttribute>() != null).ToArray();

        foreach (var type in types)
        {
            services.AddSingleton(type);

            MiniApiClass.Add(type);
        }
    }

    private WebApplication? webApplication { get; set; }

    /// <inheritdoc/>
    public override async void OnAppRun(bool IsRun)
    {
        await webApplication!.StartAsync();
    }

    /// <inheritdoc/>
    public override async void OnAppExit(bool IsRun)
    {
        await webApplication!.StopAsync();
    }
}

/// <summary>
/// 扩展
/// </summary>
public static class WebApplicationBuilderExtension
{
    internal static void AutoMapRoute(Type TargetType, WebApplication webApplication, IVampirewalMiniApiResultProvider? resultProvider = null)
    {
        var controllerAttr = TargetType.GetCustomAttribute<VampirewalControllerAttribute>();
        if (controllerAttr == null) return;

        var target = webApplication.Services.GetRequiredService(TargetType);

        foreach (var method in TargetType.GetMethods().Where(w => w.GetCustomAttribute<HttpRouterAttribute>() != null))
        {
            var handler = ExpressionTool.CreateDelegate(method, target);

            var attribute = method.GetCustomAttribute<HttpRouterAttribute>();
            if (attribute != null)
            {
                if (string.IsNullOrEmpty(attribute.path))
                {
                    attribute.path = $"{controllerAttr.Prefix}/{TargetType.Name}/{method.Name}";
                }

                if (!attribute.path.StartsWith("/"))
                {
                    attribute.path = $"/api/{attribute.path}";
                }

                #region 添加MiniApi的筛选器,使用特性的方式

                var filters = method.GetCustomAttributes<VampirewalMiniApiEndpointFilterAttribute>();

                var routerBuilder = webApplication.MapMethods(attribute.path, new[] { attribute.httpMethod.ToString() }, handler);

                if (filters != null)
                {
                    foreach (var filter in filters)
                    {
                        routerBuilder.AddEndpointFilter(filter);
                    }
                }

                #endregion

                #region 如果注册了全局统一返回值的接口的，那么这里就生效

                if (resultProvider == null)
                    continue;

                routerBuilder.AddEndpointFilter(async (context, next) =>
                {
                    var result = await next(context);

                    var data = await resultProvider.OnResponseResultProvider(result, 200);

                    return await Task.FromResult(data);
                });

                #endregion
            }
        }
    }

    /// <summary>
    /// 启用全局异常捕获中间件
    /// <para>1、必须放在第一个中间件</para>
    /// <para>2、如果没有注册<see cref="IVampirewalMiniApiResultProvider"/>的话，实际也没起作用</para>
    /// </summary>
    /// <param name="app"></param>
    public static void UseVampirewalMiniApiExceptionHandler(this IApplicationBuilder app)
    {
        app.UseMiddleware<ExceptionHandlerMiddleware>();
    }
}